<?php

namespace App\Services;

use App\SmsTask;
use App\SysConfig;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Schema;
use Exception;
use AlibabaCloud\Client\AlibabaCloud;

class SmsService
{
    const TPL_NAME_EXPERT_REVIEW_STARTED = '评审开始通知';
    const TPL_NAME_EXPERT_REVIEW_MIDTERM = '评审中期通知';
    const TPL_NAME_EXPERT_REVIEW_ENDING = '评审临近结束通知';

    private $lastErrorMessage = null;

    public function __construct()
    {
    }

    public function getLastErrorMessage()
    {
        return $this->lastErrorMessage;
    }

    /**
     * @return array 模式:
     * - ok: bool
     * - task_counts: array, 短信模板名 => 任务数， 可选
     */
    public function createSmsTasks()
    {
        $inTransaction = false;
        try {
            $sysConfig = SysConfig::loadAll();
            $sms_tasks = json_decode($sysConfig['sms_tasks']);
            $tasks = [];
            $taskCounts = [];
            $hasTaskExecuted = false;
            foreach ($sms_tasks as $taskKey => $taskConfig) {
                $scheduled_exec_at = Carbon::createFromTimeString($taskConfig->scheduled_exec_at);
                $cancel_after = Carbon::createFromTimeString($taskConfig->cancel_after);
                $now = Carbon::now();
                if (!($now >= $scheduled_exec_at && $now < $cancel_after && $taskConfig->executed_at === null)) {
                    continue;
                }
                $hasTaskExecuted = true;
                $newTasks = $this->buildSmsTasks($taskConfig);
                $taskCounts[$taskConfig->tpl_name] = count($newTasks);
                $tasks = array_merge($tasks, $newTasks);
                $taskConfig->executed_at = $now->toDateTimeString();
            }
            if ($hasTaskExecuted) {
                DB::beginTransaction();
                $inTransaction = true;
                if (!empty($tasks)) {
                    DB::table('sms_tasks')->insert($tasks);
                }
                SysConfig::where('key', 'sms_tasks')
                    ->update(['value' => json_encode($sms_tasks, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)]);
                DB::commit();
            }
            return [
                'ok' => true,
                'task_counts' => $taskCounts,
            ];
        } catch (\Exception $e) {
            Log::error('[SmsService::createSmsTasks]', ['exception' => $e]);
            if ($inTransaction) {
                try {
                    DB::rollBack();
                } catch (\Exception $e2) {
                }
            }
            return ['ok' => false];
        }
    }

    private function buildSmsTasks($taskConfig)
    {
        $tasks = [];
        $now = Carbon::now();
        $tpl_name = $taskConfig->tpl_name;
        $tpl_code = config("sms.template_codes")[$tpl_name];
        $reviewSvc = new ReviewService();
        $reviewerProjectStats = $reviewSvc->getReviewerProjectStats();
        foreach ($reviewerProjectStats as $reviewer) {
            $projects = $tpl_name == self::TPL_NAME_EXPERT_REVIEW_STARTED ?
                $reviewer['total_projects'] : $reviewer['unfinished_projects'];
            if ($projects == 0) {
                continue;
            }
            $tpl_param = [
                'username' => $reviewer['username'],
                'password' => $reviewer['password'],
                'name' => $reviewer['name'],
                'projects' => (string)$projects,
            ];
            if ($tpl_name == self::TPL_NAME_EXPERT_REVIEW_ENDING) {
                $tpl_param['days'] = (string)$taskConfig->remaining_days;
            }
            $tasks[] = [
                'created_at' => $now,
                'phone_number' => $reviewer['tel'],
                'tpl_name' => $tpl_name,
                'tpl_code' => $tpl_code,
                'tpl_param' => json_encode($tpl_param, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
                'tries' => 0,
                'last_tried_at' => '2000-01-01 00:00:00',
                'result' => '',
            ];
        }
        return $tasks;
    }

    public function createReviewStartedSmsTask($phoneNumber, $name, $projects)
    {
        $task = SmsTask::create([
            'created_at' => now(),
            'phone_number' => $phoneNumber,
            'tpl_name' => '评审开始通知',
            'tpl_code' => config('sms.template_codes.评审开始通知'),
            'tpl_param' => json_encode([
                'name' => $name,
                'projects' => (string)$projects,
            ], JSON_UNESCAPED_UNICODE),
        ]);
        return $task;
    }

    public function createReviewMiddleTermSmsTask($phoneNumber, $name, $projects)
    {
        $task = SmsTask::create([
            'created_at' => now(),
            'phone_number' => $phoneNumber,
            'tpl_name' => '评审中期通知',
            'tpl_code' => config('sms.template_codes.评审中期通知'),
            'tpl_param' => json_encode([
                'name' => $name,
                'projects' => (string)$projects,
            ], JSON_UNESCAPED_UNICODE),
        ]);
        return $task;
    }

    public function createReviewEndingSmsTask($phoneNumber, $name, $days, $projects)
    {
        $task = SmsTask::create([
            'created_at' => now(),
            'phone_number' => $phoneNumber,
            'tpl_name' => '评审临近结束通知',
            'tpl_code' => config('sms.template_codes.评审临近结束通知'),
            'tpl_param' => json_encode([
                'name' => $name,
                'days' => (string)$days,
                'projects' => (string)$projects,
            ], JSON_UNESCAPED_UNICODE),
        ]);
        return $task;
    }

    public function sendTemplateSms($phoneNumber, $templateCode, $templateParamJson)
    {
        if (!is_string($templateParamJson)) {
            throw new Exception("templateParamJson should be string");
        }
        $signName = config('sms.sign_name');
        $query = [
            'RegionId' => "cn-hangzhou",
            'PhoneNumbers' => $phoneNumber,
            'SignName' => $signName,
            'TemplateCode' => $templateCode,
            'TemplateParam' => $templateParamJson,
        ];
        Log::debug('SmsService.sendTemplateSms', ['query' => $query]);
        $res = $this->makeSmsRequest($query);
        Log::debug('SmsService.sendTemplateSms', ['res' => $res]);
        if (isset($res->Code) && $res->Code == 'OK') {
            return true;
        } else {
            throw new Exception("Bad result: " . json_encode($res, JSON_UNESCAPED_UNICODE));
        }
    }

    private function makeSmsRequest($query)
    {
        $ak = config('sms.aliyun_ak');
        AlibabaCloud::accessKeyClient($ak['access_key_id'], $ak['access_key_secret'])
            ->regionId('cn-hangzhou')
            ->asDefaultClient();
        $res = AlibabaCloud::rpc()
            ->product('Dysmsapi')
            ->version('2017-05-25')
            ->action('SendSms')
            ->method('POST')
            ->host('dysmsapi.aliyuncs.com')
            ->options([
                'query' => $query,
                'connect_timeout' => 60,
                'timeout' => 60,
            ])
            ->request();
        return json_decode(json_encode($res->toArray()));
    }
}
